/**
 * 
 */
package vn.smartware.co.framework.action;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.sql.Connection;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import vn.smartware.co.framework.bean.LoginDTO;
import vn.smartware.co.framework.common.BaseConstant;
import vn.smartware.co.framework.config.ActionMapping;
import vn.smartware.co.framework.config.ActionMappingsConfig;
import vn.smartware.co.framework.config.FormBeansConfig;
import vn.smartware.co.framework.config.SystemConfig;
import vn.smartware.co.framework.db.factory.DbResources;
import vn.smartware.co.framework.exception.ServerException;
import vn.smartware.co.framework.exception.SessionTimeOutException;
import vn.smartware.co.framework.form.ActionForm;
import vn.smartware.co.framework.plugin.FileResource;
import vn.smartware.co.java.net.commons.Utils;
import vn.smartware.co.java.net.http.JHTTPClient;


/**
 * ActionServlet
 * @author thuctt
 * created 2009/09/01
 */
public class ActionServlet extends HttpServlet {

	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 137126813235248687L;
	private static Log log = LogFactory.getLog(ActionServlet.class);
	private FileResource fileResource;
	
	/**
	 * <p>Process an HTTP "GET" request.</p>
	 * 
	 * @param request The servlet request we are processing
	 * @param responce The servlet responce we are creating
	 * 
	 * @exception IOException if an input/output error occurs
	 * @exception ServletException if a servlet exception occurs
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse responce)
			throws ServletException, IOException {
		process(request, responce);
	}

	/**
	 * <p>Process an HTTP "POST" request.</p>
	 * 
	 * @param request The servlet request we are processing
	 * @param responce The servlet responce we are creating
	 * 
	 * @exception IOException if an input/output error occurs
	 * @exception ServletException if a servlet exception occurs
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse responce)
			throws ServletException, IOException {
		process(request, responce);
	}
	
	/*************************************************************************
	 * Description:
	 * <p>Perform the standard request processing for this request, and create
	 * the corresponding responce.</p>
	 * 
	 * @param request
	 * @param responce
	 *************************************************************************/
	protected void process(HttpServletRequest request, HttpServletResponse responce)  {
	    
		try {
			fileResource = FileResource.getInstance(this.getServletContext());
			
			SystemConfig mapping = fileResource.getConfig();

			ActionForm form = preProcess(request, responce, mapping);
			
			if (!form.isStatus()) {
			
				log.debug("responce HTTP_ACCEPTED [" + HttpURLConnection.HTTP_ACCEPTED + "]");
				
				responce.setStatus(HttpURLConnection.HTTP_ACCEPTED);
	
				ServerException exception = new ServerException(form.getMessage(), form.getException());
				
				JHTTPClient.sendResponse(responce, exception);
				
			} else {
				
				log.debug("responce HTTP_OK [" + HttpURLConnection.HTTP_OK + "]");
				
				JHTTPClient.sendResponse(responce, form);
			}
			
			log.debug("Server process finished.");
			
		} catch (IOException e) {
			log.error("Return ActionForm Status Error.", e);
			e.printStackTrace();
		} catch (Throwable te) {
			te.printStackTrace();
		} 
	}
	
	/**
	 * when tomcat server startup, this method was called
	 * @throws ServletException 
	 */
	public void init() throws ServletException {
		super.init();
		try{
			// get connection and test it
			DbResources dbResource = DbResources.getInstance();
			Connection con = dbResource.getDatasource().getConnection();
			if (null == con){
				throw new ServletException();
			}else{
				con.close();
				con = null;
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * processProcesser: 
	 * @param request
	 * @param responce
	 * @param mapping
	 * @return ActionForm
	 */
	@SuppressWarnings("rawtypes")
	protected ActionForm preProcess(HttpServletRequest request,
			HttpServletResponse responce, SystemConfig mapping) {
		
		ActionForm form = null;
		
		try {

			form = (ActionForm)JHTTPClient.rcvRequest(request);
			
			if (Utils.isNullEmpty(form)) {
				log.error("ActionForm is null.");
				return ActionMapping.setExceptionMessage(this.getServletContext(), form, 
						new ServerException("ActionForm is null"), mapping);
			}
			
			String mappingSession = mapping.getActionMappingConfig(form.getActionForm()).getScope();
			
			if (mappingSession == null || mappingSession.equals(BaseConstant.APP_SESSION)) {
				form = sessionManager(request, responce, form);
			} else if (mappingSession.equals(BaseConstant.SESSION_SESSION)) {
				int timeOut = Integer.parseInt(mapping.getActionMappingConfig(form.getActionForm()).getTime());
				longSessionManager(request, responce, timeOut);
			}
						
//			ValidatorForm validator = new ValidatorForm();
//			ValidatorResources validatorResources = fileResource.getValidator();
//			validator.validate(form, validatorResources, this.getServletContext());
//			log.debug("Validator complete.");
			
			log.debug("ActionForm = " + form.getActionForm());
			log.debug("ActionMethod = " + form.getActionMethod());
			
			FormBeansConfig formBean = mapping.getFormBeans(form.getActionForm());

			if (formBean == null) {
				log.error("Mapping FormBean Config fail: " + form.getActionForm());
				return ActionMapping.setExceptionMessage(this.getServletContext(), form, 
						                          new ServerException(), "FormBean Mapping failed.");
			}
			
			ActionMappingsConfig action = mapping.getActionMappingConfig(form.getActionForm());
			
			log.debug("Action Class = " + action.getType());
			
			Class cls = Class.forName(action.getType());

			BaseAction service = (BaseAction)cls.newInstance();

			service.setContext(this.getServletContext());
			
			if (form.getActionMethod() == null) {
				log.debug("Execute Method call.");
				form = service.execute(request, responce, mapping, form);
				
			} else {
				
				log.debug("User Method call.");
				
				Class[] args = new Class[]{HttpServletRequest.class, HttpServletResponse.class, SystemConfig.class, ActionForm.class};
				Method method = service.getClass().getDeclaredMethod(form.getActionMethod(), args);
				
				form = (ActionForm) method.invoke(service, new Object[]{request, responce, mapping, form});
			}

			return form;
			
		} catch (NoSuchMethodException e) {
			log.error("Method NotFound.", e);
			return ActionMapping.setExceptionMessage(this.getServletContext(), form, e, "Method NotFound.");
		} catch (InvocationTargetException e) {
			e.getTargetException().printStackTrace();
			log.error("Reflect UserMethod InsideError. Check ServerSide log. call Method=" + form.getActionMethod());
			return ActionMapping.setExceptionMessage(this.getServletContext(), form, e.getTargetException(), e.getTargetException().getMessage());			    
		} catch (ClassNotFoundException e) {
			log.error("Bean NotFound.", e);
			return ActionMapping.setExceptionMessage(this.getServletContext(), form, e, "Bean NotFound.");
		} catch (IllegalAccessException e) {
			log.error("Service Instance Error.", e);
			return ActionMapping.setExceptionMessage(this.getServletContext(), form, e, "Service Instance Error.");
		} catch (InstantiationException e) {
			log.error("Service Instance Error.", e);
			return ActionMapping.setExceptionMessage(this.getServletContext(), form, e, "Service Instance Error.");
		} catch (IOException e) {
			log.error("InputStream get Error.", e);
			return ActionMapping.setExceptionMessage(this.getServletContext(), form, e, "InputStream get Error.");
		} catch (SessionTimeOutException e) {
		    log.warn("Session Time out.", e);
		    return ActionMapping.setExceptionMessage(this.getServletContext(), form, e, "Session Time out.");
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			return ActionMapping.setExceptionMessage(this.getServletContext(), form, e, e.getMessage());
		}
	}
	
	/**
	 * 
	 * sessionManager: 
	 * @param request
	 * @param responce
	 * @param form
	 * @return ActionForm
	 * @throws SessionTimeOutException
	 */
	protected ActionForm sessionManager(HttpServletRequest request, HttpServletResponse responce,
			                             ActionForm form) throws SessionTimeOutException {
		
		HttpSession session = request.getSession(true);
		LoginDTO user = (LoginDTO)session.getAttribute("userInfo");
		
		boolean sessionIsNew = session.isNew();
		form.setSession(sessionIsNew);

		log.debug("Client send SessionID = [" + request.getRequestedSessionId() + "]");
		log.debug("Connect IP = [" + ((LoginDTO)form.getUserDTO()).getIpAddress() + "]");
		
		if (request.getRequestedSessionId() != null && sessionIsNew) {
		    log.debug("[SessionTimeOutException thrown.]");
		    SessionTimeOutException timeoutException = new SessionTimeOutException();
		    timeoutException.setNewSessionID(session.getId());
		    throw timeoutException;
		}
		
		log.debug("[Session OK.]");
		
		if (user == null) {
			user = (LoginDTO)form.getUserDTO();
			session.setAttribute("userInfo", user);
		}
		
		return form;
	}
	
	/**
	 * 
	 * longSessionManager: 
	 * @param request
	 * @param responce
	 * @param timeOut
	 */
	protected void longSessionManager(HttpServletRequest request,
			HttpServletResponse responce, int timeOut) {
		HttpSession session = request.getSession(true);
		session.setMaxInactiveInterval(timeOut);
	}

}
